#ifndef REGIONGROWING_CPP
#define REGIONGROWING_CPP

#include "RegionGrowing.h"
#include "Transformations.h"


/**
  * This class represents the segmentation technique RegionGrowing
  */

float RegionGrowing::getAvg(QImage * img, QList<int> * pixels)
{
    float avg = 0;
    int pixel;

    foreach (pixel, *pixels)
        avg += (float) Transformations::getIntensity(img, pixel);

    return (float) avg / pixels->size();
}

float RegionGrowing::getStandardDeviation(QImage * img, QList<int> * pixels)
{
    float avg = getAvg(img, pixels);

    int pixel;

    float desv = 0;

    foreach (pixel, *pixels)
        desv += (float) (Transformations::getIntensity(img, pixel) - avg) * (Transformations::getIntensity(img, pixel) - avg);

    desv = (float) desv / pixels->size();

    return (float) sqrt(desv);
}

bool RegionGrowing::accepted( int pixel, float tolerance, float avgIntensity, float proportion)
{
    // Obtain the neighbors pixel
    QList<int> * neighbors = Transformations::getNeighboringPixels( this->getImage(), pixel );

    int neighbor = 0;
    float cont = 0;
    // Count how many neighbors satisfy the criterion
    foreach (neighbor, *neighbors)
        if ( fabs( Transformations::getIntensity(image, neighbor) - avgIntensity) <= tolerance )
            cont++;

    int size = neighbors->size();
    delete neighbors;

    // Verify that the proportion of neighbors which satisfy the criterion, is more than the established proportion
    if ( (float) (cont / size) > proportion)
        return true;
    else
        return false;
}

float RegionGrowing::calculateTolerance(QMultiMap<float, int> * seeds)
{
    float sum = 0;

    // For each seed in the seeds map...
    for (QMultiMap<float, int>::iterator i = seeds->begin(); i != seeds->end(); i++)
    {
        QList<int> * neighbors = Transformations::getNeighboringPixels(image, i.value());
        sum = sum + getStandardDeviation(image, neighbors);
        delete neighbors;
    }

    return (float) sum / seeds->size();
}

float RegionGrowing::getQualityFactor( int pixel, float avgIntensity )
{
    // Obtain the neighbors pixel
    QList<int> * neighbors = Transformations::getNeighboringPixels( image, pixel );

    int neighbor = 0;
    float sum = 0;
    // Calculate the sum of all differences between neighbors intensities and
    foreach (neighbor, *neighbors)
        sum += fabs( Transformations::getIntensity(image, neighbor) - avgIntensity);


    int size = neighbors->size();
    delete neighbors;

    // Return the average...
    return sum / size;
}

RegionGrowing::RegionGrowing()
{
    nonVisitedPixels = new QMultiMap<float, int>;
    autoTolerance = true;
    tol = 1;
    prop = 0.8;
}

void RegionGrowing::addSeedPoint( int xI, int yI )
{
    nonVisitedPixels->insert(0, Transformations::getPixelNumber(image, xI, yI));
}

Segmentation * RegionGrowing::segment()
{
    //If there is any seed...
    if ( nonVisitedPixels->size() != 0 )
    {
        float tolerance;
        // If the user didn't specify a tolerance, it calculates the tolerance to consider a pixel similar to his neighbor.
        if (autoTolerance)
            tolerance = calculateTolerance(nonVisitedPixels);
        else
            tolerance = tol;
        tolerance = 50;

        // Calculate the initial intensity value as the average of the seeds intensity
        float avgIntensity = getAvg( this->getImage(), &nonVisitedPixels->values() );

        // Minimum proportion of neighboring pixels which intensity difference with the avgIntensity have to be less than the
        // tolerance in order to include it into the region
        float proportion = prop;

        // Build the segmentation container
        Segmentation * s = new Segmentation(image);

        QSet<int> * visitedPixels = new QSet<int>;

        int pixelNumber = 0;
        float avgDiff = 0;

        // Create the empty Region
        SegmentedObject * region = new SegmentedObject(s);

        while ( !nonVisitedPixels->empty() )
        {
            // Obtain the first element of the map
            pixelNumber = nonVisitedPixels->begin().value();
            avgDiff = nonVisitedPixels->begin().key();

            nonVisitedPixels->remove(avgDiff, pixelNumber);

            // Add the visited pixel to the region
            region->addPixel(pixelNumber);

            // Mark the visited pixel as visited
            visitedPixels->insert(pixelNumber);

            // Obtain the neighbors of the pixel
            QList<int> * neighbors = Transformations::getNeighboringPixels( this->getImage(), pixelNumber );
            int neighbor = 0;

            foreach ( neighbor, *neighbors )
            {
                // If it has not been visited yet...
                if ( !visitedPixels->contains(neighbor) )
                {
                    // Mark the visited pixel as visited
                    visitedPixels->insert(neighbor);
                    // If the neighbor satisfies the acceptance criterion..
                    if ( accepted(neighbor, tolerance, avgIntensity, proportion) )
                    {
                        // Add it to the list of nonVisitedPixels. Then, it will be added to the region.
                        nonVisitedPixels->insert( getQualityFactor(neighbor, avgIntensity), neighbor);
                    }
                    else
                    {
                        // Insert into the frontier
                        region->addPixelToFrontier(neighbor);
                    }
                }
            }

            delete neighbors;
        }

        delete visitedPixels;
//        qDebug() << "Pixels in the segmented region: " << region->getSegmentedObject()->size();
//        qDebug() << "Tolerance: " << tolerance;
//        qDebug() << "Average Intesity: " << avgIntensity;
//        qDebug() << "Proportion: " << proportion;

        // Build and return the segmentation

        s->addSegmentedObject(region);

        return s;
    }
    else
    {
        return new Segmentation(image);
    }
}

#endif // REGIONGROWING_CPP

